/*
 * calculation_server_test.cpp
 *
 *  Created on: 21/06/2011
 *      Author: ofer
 */
#include "calculation_server.h"
#include "Polynomial.h"
#include "SolveEquationSystem.h"
#include "Similarity.h"
#include "mtmtest.h"
#include "matrix.h"
#include <stdarg.h>

Matrix initMatrix(int rows, int columns, ...) {
	Matrix result(rows, columns);
	va_list vl;
	va_start( vl, columns );
	for (int r = 1; r <= rows; ++r) {
		for (int c = 1; c <= columns; ++c) {
			double cell = va_arg(vl,double);
			result(r, c) = cell;
		}
	}
	va_end( vl );
	return result;
}
bool testCtorDtor(){
	CalculationServer server;
	return true;
}
bool testAddJob(){
	CalculationServer server;

	Matrix B = initMatrix(3,3,
			1.0, 2.0, 1.0,
			3.0, 4.0, 3.0,
			4.0, 5.0, 1.0);
	Matrix A = initMatrix(3,3,
			-1.83, 0.5, 0.33,
			1.5, -0.5, 0.0,
			-0.17, 0.5, -0.33);
	int job1, job2, job3, job4, job5, job6;

	// check if total jobs number is 0
	ASSERT_EQUALS(0,server.totalJobs());

	// add legal jobs
	ASSERT_NO_THROW(job1 = server.addJob(CalculationServer::SOLVE_EQUATION_SYSTEM,23,A,B));
	ASSERT_NO_THROW(job2 = server.addJob(CalculationServer::SIMILARITY,24,A,B));
	ASSERT_NO_THROW(job3 = server.addJob(CalculationServer::POLYNOMIAL,25,A,B));

	// verify all ID's are different
	ASSERT_EQUALS(job1!=job2, true);
	ASSERT_EQUALS(job2!=job3, true);
	ASSERT_EQUALS(job1!=job3, true);

	//check if total jobs number is 3
	ASSERT_EQUALS(3,server.totalJobs());

	//try to add illegal priority
	ASSERT_THROW(CalculationServer::InvalidPriority,job1 =
			server.addJob(CalculationServer::SOLVE_EQUATION_SYSTEM,-1,A,B));
	ASSERT_THROW(CalculationServer::InvalidPriority,job1 =
			server.addJob(CalculationServer::SOLVE_EQUATION_SYSTEM,101,A,B));

	// check if total jobs number was not changed
	ASSERT_EQUALS(3,server.totalJobs());

	//add similar jobs (should be possible)
	ASSERT_NO_THROW(job4 = server.addJob(CalculationServer::SOLVE_EQUATION_SYSTEM,23,A,B));
	ASSERT_NO_THROW(job5 = server.addJob(CalculationServer::SIMILARITY,24,A,B));
	ASSERT_NO_THROW(job6 = server.addJob(CalculationServer::POLYNOMIAL,25,A,B));
	return true;
}
bool testRemoveJob(){
	CalculationServer server;

	Matrix B = initMatrix(3,3,
			1.0, 2.0, 1.0,
			3.0, 4.0, 3.0,
			4.0, 5.0, 1.0);
	Matrix A = initMatrix(3,3,
			-1.83, 0.5, 0.33,
			1.5, -0.5, 0.0,
			-0.17, 0.5, -0.33);

	int job1, job2;

	//no jobs to remove, should throw accordingly
	ASSERT_THROW(CalculationServer::JobNotFound,server.removeJob(0));

	// adds two jobs to remove later
	ASSERT_NO_THROW(job1 =
			server.addJob(CalculationServer::SOLVE_EQUATION_SYSTEM,60,A,B));
	ASSERT_NO_THROW(job2 = server.addJob(CalculationServer::SIMILARITY,50,A,B));

	// make sure list is correctly updated before start of test removing
	ASSERT_EQUALS(job1,server.nextJob());
	ASSERT_EQUALS(2,server.totalJobs());

	//remove first job
	ASSERT_NO_THROW(server.removeJob(job1));

	//cannot remove a job that was removed already
	ASSERT_THROW(CalculationServer::JobNotFound,server.removeJob(job1));

	//check list update
	ASSERT_EQUALS(job2,server.nextJob());
	ASSERT_EQUALS(1,server.totalJobs());

	//remove second job, check list update (should be empty)
	ASSERT_NO_THROW(server.removeJob(job2));
	ASSERT_THROW(CalculationServer::JobNotFound,server.nextJob());
	ASSERT_EQUALS(0,server.totalJobs());

	//cannot remove job while list is empty
	ASSERT_THROW(CalculationServer::JobNotFound,server.removeJob(job2));

	//refill list with jobs
	ASSERT_NO_THROW(job1 =
			server.addJob(CalculationServer::SOLVE_EQUATION_SYSTEM,60,A,B));
	ASSERT_NO_THROW(job2 = server.addJob(CalculationServer::SIMILARITY,50,A,B));

	//remove jobs again, jobs are in a different order
	ASSERT_NO_THROW(server.removeJob(job2));
	ASSERT_EQUALS(job1,server.nextJob());
	ASSERT_EQUALS(1,server.totalJobs());
	ASSERT_NO_THROW(server.removeJob(job1));
	ASSERT_THROW(CalculationServer::JobNotFound,server.nextJob());
	ASSERT_EQUALS(0,server.totalJobs());
	return true;
}
bool testChangePriority() {
	CalculationServer server;
	Matrix B = initMatrix(3,3,
			1.0, 2.0, 1.0,
			3.0, 4.0, 3.0,
			4.0, 5.0, 1.0);
	Matrix A = initMatrix(3,3,
			-1.83, 0.5, 0.33,
			1.5, -0.5, 0.0,
			-0.17, 0.5, -0.33);

	// Add jobs
	int job1 = server.addJob(CalculationServer::POLYNOMIAL, 30, A, B);
	int job2 = server.addJob(CalculationServer::SOLVE_EQUATION_SYSTEM, 40,
			Matrix::createUnit(4), B);
	int job3 = server.addJob(CalculationServer::SIMILARITY, 55, A, A);
	int job4 = server.addJob(CalculationServer::SIMILARITY,60, A, A);
	int job5 = server.addJob(CalculationServer::SOLVE_EQUATION_SYSTEM, 76,
			Matrix::createUnit(4), B);
	int job6 = server.addJob(CalculationServer::SOLVE_EQUATION_SYSTEM, 98,
			Matrix::createUnit(4), B);
	int job7 = server.addJob(CalculationServer::POLYNOMIAL, 98, A, B);
	int job8 = server.addJob(CalculationServer::POLYNOMIAL, 99, A, B);

	// Jobs order should be: job8 -> job6 -> job7 -> job5 -> job4 -> job3 -> job2 -> job1
	ASSERT_EQUALS(job8, server.nextJob());

	// doesn't change job queue, including priority
	ASSERT_THROW(CalculationServer::InvalidPriority, server.changePriority(job4, -10));
	ASSERT_EQUALS(job8, server.nextJob());

	//put job8 at end of queue
	ASSERT_NO_THROW(server.changePriority(job8,29));
	//  job6 -> job7 -> job5 -> job4 -> job3 -> job2 -> job1 -> job8
	//Now job8 is the last in queue, and job6 is first
	ASSERT_EQUALS(job6, server.nextJob());

	//move job7 to top of queue
	ASSERT_NO_THROW(server.changePriority(job7,100));
	//  job7 -> job6 -> job5 -> job4 -> job3 -> job2 -> job1 -> job8
	ASSERT_EQUALS(job7, server.nextJob());

	//give job6 the same priority as job7.
	ASSERT_NO_THROW(server.changePriority(job6,100));
	//  job6 -> job7 -> job5 -> job4 -> job3 -> job2 -> job1 -> job8
	ASSERT_EQUALS(job6, server.nextJob()); //job6 is first cause it's been
										   //entered first

	//set job5 priority also to 100, which should put it before job6
	ASSERT_NO_THROW(server.changePriority(job5,100));
	//  job5 -> job6 -> job7 -> job4 -> job3 -> job2 -> job1 -> job8
	ASSERT_EQUALS(job5, server.nextJob()); //job5 is first

	//set job4 priority also to 100, which should put it before job5
	ASSERT_NO_THROW(server.changePriority(job4,100));
	//  job4 -> job5 -> job6 -> job7 -> job3 -> job2 -> job1 -> job8
	ASSERT_EQUALS(job4, server.nextJob()); //job4 is first

	//execute all jobs to verify the order
	ASSERT_NO_THROW(server.removeJob(job4));
	//job5 -> job6 -> job7 -> job3 -> job2 -> job1 -> job8
	ASSERT_EQUALS(job5, server.nextJob()); //job2 is first
	ASSERT_NO_THROW(server.removeJob(job5));
	//job6 -> job7 -> job3 -> job2 -> job1 -> job8
	ASSERT_EQUALS(job6, server.nextJob()); //job6 is first
	ASSERT_NO_THROW(server.removeJob(job6));
	//job7 -> job3 -> job2 -> job1 -> job8
	ASSERT_EQUALS(job7, server.nextJob()); //job7 is first
	ASSERT_NO_THROW(server.removeJob(job7));
	//job3 -> job2 -> job1 -> job8
	ASSERT_EQUALS(job3, server.nextJob()); //job3 is first
	ASSERT_NO_THROW(server.removeJob(job3));
	//job2 -> job1 -> job8
	ASSERT_EQUALS(job2, server.nextJob()); //job2 is first
	ASSERT_NO_THROW(server.removeJob(job2));
	//job1 -> job8
	ASSERT_EQUALS(job1, server.nextJob()); //job1 is first
	ASSERT_NO_THROW(server.removeJob(job1));
	//job8
	ASSERT_EQUALS(job8, server.nextJob()); //job8 is first
	ASSERT_NO_THROW(server.removeJob(job8)); //empty list
	ASSERT_THROW(CalculationServer::JobNotFound,server.nextJob());

	//Test throwing...
	job3 = server.addJob(CalculationServer::SIMILARITY, 55, A, A);
	//Illegal job
	ASSERT_THROW(CalculationServer::JobNotFound,server.changePriority(1000,100));

	//Illegal Priority
	ASSERT_THROW(CalculationServer::InvalidPriority,server.changePriority(job3,-1));

	//Both
	ASSERT_THROW(CalculationServer::JobNotFound,server.changePriority(150,-1));

	//0 is a legal priority!
	ASSERT_NO_THROW(server.changePriority(job3,0));

	return true;

}

bool testExecute() {
	CalculationServer server;
	Matrix A = initMatrix(2, 2,
			1.0, 1.0,
			1.0, 0.0);
	Matrix B = initMatrix(4, 1,
			2.0,
			0.0,
			1.0,
			-1.0);
	Matrix C = initMatrix(3,3,
			1.0, 2.0, 1.0,
			3.0, 4.0, 3.0,
			4.0, 5.0, 1.0);
	Matrix D = initMatrix(2, 2,
			1.0, -1.0,
			-1.0, 2.0);
	//Execute when no jobs are in queue
	ASSERT_THROW(CalculationServer::JobNotFound, server.execute());

	// Add jobs
	int job1 = server.addJob(CalculationServer::POLYNOMIAL, 50, A, B);
	int job2 = server.addJob(CalculationServer::SOLVE_EQUATION_SYSTEM, 49,
			Matrix::createUnit(4), B);
	int job3 = server.addJob(CalculationServer::SIMILARITY, 80, A, A);
	int job4 = server.addJob(CalculationServer::POLYNOMIAL, 49, C, B);

	// Jobs order should be: job3 ->job1 -> job2 -> job4

	//executed job should be job3
	ASSERT_NO_THROW(server.execute());
	// Jobs order should be:job1 -> job2 -> job4
	ASSERT_EQUALS(server.totalJobs(),3);
	ASSERT_THROW(CalculationServer::JobNotFound, server.removeJob(job3));

	//Check that The order hasn't changed, and that it s correct
	ASSERT_EQUALS(job1, server.nextJob());

	//Add a new job with same priority as job1
	int job6 = server.addJob(CalculationServer::SIMILARITY, 50, A,
			Matrix::createUnit(5));
	// Jobs order should be: job1 -> job6 -> job2 -> job4
	Matrix output = server.execute(); //execute job1
	// Jobs order should be: job6 -> job2 -> job4
	ASSERT_EQUALS(D, output);
	ASSERT_EQUALS(server.totalJobs(),3);
	ASSERT_THROW(CalculationServer::JobNotFound, server.removeJob( job1));
	ASSERT_EQUALS(job6, server.nextJob());

	//now executed job should be job6, which should throw
	ASSERT_THROW(CalculationServer::OperationFailed, server.execute());
	// Jobs order should be: job2 -> job4
	ASSERT_EQUALS(server.totalJobs(),2);
	ASSERT_THROW(CalculationServer::JobNotFound, server.removeJob( job6));
	ASSERT_EQUALS(job2, server.nextJob());

	//execute last jobs
	ASSERT_NO_THROW(server.execute());
	// Jobs order should be: job4
	ASSERT_EQUALS(job4, server.nextJob());
	ASSERT_EQUALS(server.totalJobs(),1);
	ASSERT_NO_THROW(server.execute());
	// Jobs order should be: empty
	ASSERT_EQUALS(server.totalJobs(),0);
	ASSERT_THROW(CalculationServer::JobNotFound, server.removeJob( job4));

	//no more jobs!
	ASSERT_THROW(CalculationServer::JobNotFound, server.execute());
	return true;

}

bool testNextJob() {
	CalculationServer server;
	Matrix A = initMatrix(2, 2,
			1.0, 1.0,
			1.0, 0.0);
	Matrix B = initMatrix(4, 1,
			2.0,
			0.0,
			1.0,
			-1.0);

	//No next job, should throw.
	ASSERT_THROW(CalculationServer::JobNotFound,server.nextJob());

	// Add jobs
	int job1 = server.addJob(CalculationServer::POLYNOMIAL, 50, A, B);
	ASSERT_EQUALS(job1,server.nextJob());
	int job2 = server.addJob(CalculationServer::SOLVE_EQUATION_SYSTEM, 50,
			Matrix::createUnit(4), B);
	ASSERT_EQUALS(job1,server.nextJob());
	int job3 = server.addJob(CalculationServer::SIMILARITY, 80, A, A);
	ASSERT_EQUALS(job3,server.nextJob());

	// Jobs order should be: job3 -> job1 -> job2

	//nextJob after execution
	server.execute();
	ASSERT_EQUALS(job1,server.nextJob()); // job1 -> job2

	//next job after changing priority
	ASSERT_NO_THROW(server.changePriority(job1,10));
	ASSERT_EQUALS(job2,server.nextJob()); // job2 -> job1

	//after deleting
	ASSERT_NO_THROW(server.removeJob(job2));
	ASSERT_EQUALS(job1,server.nextJob()); // job1

	//Delete All
	ASSERT_NO_THROW(server.execute());
	ASSERT_THROW(CalculationServer::JobNotFound,server.nextJob());
	return true;

}


bool testTotalJobs(){
	CalculationServer server;
	Matrix A = initMatrix(2, 2,
			1.0, 1.0,
			1.0, 0.0);
	Matrix B = initMatrix(4, 1,
			2.0,
			0.0,
			1.0,
			-1.0);
	Matrix C = initMatrix(3,3,
			1.0, 2.0, 1.0,
			3.0, 4.0, 3.0,
			4.0, 5.0, 1.0);

	//list is empty
	ASSERT_EQUALS(0,server.totalJobs()); // total jobs = 0

	//adding jobs
	ASSERT_NO_THROW(server.addJob(CalculationServer::POLYNOMIAL, 50, A, B));
	ASSERT_EQUALS(1,server.totalJobs()); // total jobs = 1
	ASSERT_NO_THROW(server.addJob(CalculationServer::SOLVE_EQUATION_SYSTEM, 49,
			Matrix::createUnit(4), B));
	ASSERT_EQUALS(2,server.totalJobs()); // total jobs = 2
	ASSERT_NO_THROW(server.addJob(CalculationServer::SIMILARITY, 80, A, A));
	ASSERT_EQUALS(3,server.totalJobs()); // total jobs = 3
	ASSERT_NO_THROW(server.addJob(CalculationServer::POLYNOMIAL, 49, C, B));
	ASSERT_EQUALS(4,server.totalJobs()); // total jobs = 4

	//illegal job entered
	ASSERT_THROW(CalculationServer::InvalidPriority,
			server.addJob(CalculationServer::SOLVE_EQUATION_SYSTEM,-1,A,B));
	ASSERT_EQUALS(4,server.totalJobs()); // total jobs = 4, total jobs unchanged

	//remove job
	ASSERT_NO_THROW(server.removeJob(server.nextJob()));
	ASSERT_EQUALS(3,server.totalJobs()); // total jobs = 3

	//execute job
	ASSERT_NO_THROW(server.execute());
	ASSERT_EQUALS(2,server.totalJobs()); // total jobs = 2


	// executing last jobs
	ASSERT_NO_THROW(server.execute());
	ASSERT_EQUALS(1,server.totalJobs()); // total jobs = 1
	ASSERT_NO_THROW(server.execute());
	ASSERT_EQUALS(0,server.totalJobs()); // total jobs = 0
	return true;
}
int main() {
	RUN_TEST(testCtorDtor);
	RUN_TEST(testAddJob);
	RUN_TEST(testRemoveJob);
	RUN_TEST(testChangePriority);
	RUN_TEST(testExecute);
	RUN_TEST(testNextJob);
	RUN_TEST(testTotalJobs);
	return (EXIT_SUCCESS);

}
